#include "bmp280Driver.h"

#define Sensor_CNT 60                                       // 设备实例数量
static BMP280DriverInstance_s *BMP280Instance[Sensor_CNT]; // 设备实例数组

/**
 * @brief BMP280写入字节
 *
 * @param bmp280 BMP280传感器实例
 * @param reg 要写入的寄存器地址
 * @param value 要写入的值
 * @return void
 */
void BMP280WriteByte(BMP280DriverInstance_s *bmp280, uint8_t reg, uint8_t value)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t tx_buffer[2];
    tx_buffer[0] = reg;                                                               // 寄存器地址
    tx_buffer[1] = value;                                                             // 寄存器值
    IICTransmit(bmp280->iic_instance, tx_buffer, sizeof(tx_buffer), IIC_SEQ_HOLDON); // 发送数据
}

/**
 * @brief BMP280读取字节
 *
 * @param bmp280 BMP280传感器实例
 * @param reg 要读取的寄存器地址
 * @param data 存储读取数据的缓冲区
 * @param size 要读取的数据大小
 * @return void
 */
void BMP280ReadByte(BMP280DriverInstance_s *bmp280, uint8_t reg, uint8_t *data, uint16_t size)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t regtmp = reg | 0x80; // 设置读操作标志位

    IICTransmit(bmp280->iic_instance, &reg, sizeof(reg), IIC_SEQ_HOLDON); // 发送寄存器地址
    IICReceive(bmp280->iic_instance, data, size, IIC_SEQ_HOLDON);         // 读取数据
    // DWT_Delay(0.01);                                                       // 延时，确保读取完成
}

/**
 * @brief 设置BMP280的待机时间和滤波器系数
 *
 * @param bmp280 BMP280传感器实例
 * @param filter_coefficient 滤波器系数BMP280_FILTER_COEFFICIENT_e
 * @param t_sb 待机时间BMP280_T_SB_e
 * @return void
 * @note 该函数用于设置BMP280的待机时间和滤波器系数。待机时间决定了传感器在进入低功耗模式时的等待时间，滤波器系数用于平滑传感器输出的数据。同时，设置SPI_EN为0，表示使用IIC通信方式。函数首先检查传入的指针是否有效，然后根据传入的参数计算出寄存器值，并通过IIC接口写入到BMP280的配置寄存器中。
 */
void BMP280SetStandbyFilter(BMP280DriverInstance_s *bmp280, BMP280_FILTER_COEFFICIENT_e filter_coefficient, BMP280_T_SB_e t_sb)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t regtmp = ((t_sb << 5) | (filter_coefficient << 2) | 0); // SPI_EN设置为0，默认使用IIC
    BMP280WriteByte(bmp280, BMP280_CONFIG_REG, regtmp);             // 写入配置寄存器
}

/**
 * @brief 校准BMP280传感器
 *
 * @param bmp280 BMP280传感器实例
 * @return void
 * @note 该函数用于校准BMP280传感器。它通过读取传感器的修正参数寄存器，将读取到的值存储到全局变量bmp280->trimming_parameter中，以便后续计算温度和气压时使用。
 */
void BMP280Calibrate(BMP280DriverInstance_s *bmp280)
{
    uint8_t data[2] = {0};
    BMP280ReadByte(bmp280, BMP280_DIG_T1_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_T1 = (uint16_t)((data[1] << 8) | data[0]);  // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_T2_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_T2 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_T3_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_T3 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P1_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P1 = (uint16_t)((data[1] << 8) | data[0]);  // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P2_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P2 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P3_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P3 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P4_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P4 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P5_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P5 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P6_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P6 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P7_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P7 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P8_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P8 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);                                                  // 延时，确保读取完成

    BMP280ReadByte(bmp280, BMP280_DIG_P9_LSB_REG, data, sizeof(data)); // 读取修正参数
    bmp280->trimming_parameter.dig_P9 = (int16_t)((data[1] << 8) | data[0]);   // 组合修正参数
    DWT_Delay(0.0001);     
}

/**
 * @brief 获取BMP280的ID
 *
 * @param bmp280 BMP280传感器实例
 * @param id 存储ID的缓冲区
 * @return void
 * @note 该函数用于获取BMP280的ID。用于测试通信
 */
void BMP280GetID(BMP280DriverInstance_s *bmp280, uint8_t *id)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL || id == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t data[2] = {0};
    BMP280ReadByte(bmp280, BMP280_CHIPID_REG, data, sizeof(data)); // 读取ID寄存器
    *id = data[0];                                                 // 获取ID
    // BMP280ReadByte(bmp280, BMP280_CHIPID_REG, id, sizeof(*id)); // 读取ID寄存器
}

/**
 * @brief BMP280复位
 *
 * @param bmp280 BMP280传感器实例
 */
void BMP280Reset(BMP280DriverInstance_s *bmp280)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL)
    {
        return; // 检查指针是否有效
    }
    BMP280WriteByte(bmp280, BMP280_RESET_REG, 0xB6); // 写入复位命令
    // DWT_Delay(0.1);                                  // 延时，确保复位完成
}
/**
 * @brief 设置BMP280的过采样率和工作模式
 *
 * @param bmp280 BMP280传感器实例
 * @param p_oversampling 气压过采样率BMP280_P_OVERSAMPLING_e
 * @param t_oversampling 温度过采样率BMP280_T_OVERSAMPLING_e
 * @param work_mode 工作模式BMP280_WORK_MODE_e
 * @return void
 * @note 该函数用于设置BMP280的过采样率和工作模式。根据传入的参数计算出寄存器值，并通过IIC接口写入到BMP280的控制寄存器中。
 */
void BMP280SetOversamplingAndMode(BMP280DriverInstance_s *bmp280, BMP280_P_OVERSAMPLING_e p_oversampling, BMP280_T_OVERSAMPLING_e t_oversampling, BMP280_WORK_MODE_e work_mode)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t regtmp = ((t_oversampling << 5) | (p_oversampling << 2) | bmp280->work_mode); // 设置工作模式
    BMP280WriteByte(bmp280, BMP280_CTRLMEAS_REG, regtmp);                                 // 写入控制寄存器
}


/**
 * @brief 初始化BMP280驱动实例
 *
 * @param conf BMP280驱动配置结构体
 * @return BMP280DriverInstance_s* 返回BMP280驱动实例指针
 * @note 该函数用于初始化BMP280驱动实例。根据传入的配置结构体，创建BMP280驱动实例，并进行必要的初始化操作。
 */
void BMP280GetTemperature(BMP280DriverInstance_s *bmp280, float *temperature)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL || temperature == NULL)
    {
        return; // 检查指针是否有效
    }
    uint8_t data[3] = {0};
    BMP280ReadByte(bmp280, BMP280_TEMPERATURE_MSB_REG, data, sizeof(data)); // 读取温度数据
    // 根据温度adc值与校准值进行温度计算
    // 公式来源于datasheet
    int32_t adc_T = ((int32_t)data[0] << 12) | ((int32_t)data[1] << 4) | (data[2] >> 4); // 组合数据
    double var1, var2, t_fine;
    var1 = ((double)adc_T / 16384.0 - (double)bmp280->trimming_parameter.dig_T1 / 1024.0) * (double)bmp280->trimming_parameter.dig_T2; // 计算中间变量
    var2 = (((double)adc_T / 131072.0 - (double)bmp280->trimming_parameter.dig_T1 / 8192.0) * ((double)adc_T / 131072.0 -
                                                                                       (double)bmp280->trimming_parameter.dig_T1 / 8192.0)) *
           (double)bmp280->trimming_parameter.dig_T3;      // 计算中间变量
    t_fine = (int32_t)(var1 + var2);               // 计算t_fine
    bmp280->measure.t_fine = t_fine;               // 存储t_fine值
    bmp280->measure.temperature = (float)(var1 + var2) / 5120.0f; // 存储温度值
    *temperature = (float)(var1 + var2) / 5120.0f; // 计算实际温度值
}


/**
 * @brief 获取BMP280的气压值
 *
 * @param bmp280 BMP280传感器实例
 * @param pressure 存储气压值的缓冲区
 * @return void
 * @note 该函数用于获取BMP280的气压值。通过读取气压寄存器，将读取到的值转换为实际的气压值，并存储到传入的缓冲区中。
 */
void BMP280GetPressure(BMP280DriverInstance_s *bmp280, float *pressure)
{
    if (bmp280 == NULL || bmp280->iic_instance == NULL || pressure == NULL)
    {
        return; // 检查指针是否有效
    }
    BMP280GetTemperature(bmp280, &bmp280->measure.temperature); // 获取温度值
    // DWT_Delay(0.01);                                            // 延时，确保读取完成
    uint8_t data[3] = {0};
    BMP280ReadByte(bmp280, BMP280_PRESSURE_MSB_REG, data, sizeof(data));                 // 读取气压数据
    int32_t adc_P = ((int32_t)data[0] << 12) | ((int32_t)data[1] << 4) | (data[2] >> 4); // 组合数据
    double var1, var2, p_fine, t_fine;
    t_fine = bmp280->measure.t_fine; // 获取t_fine值
    
    
    
    // adc_P = 415148; // 测试数据
    // t_fine = 128422; // 测试数据
    // bmp280->trimming_parameter.dig_P1 = 36477; // 测试数据
    // bmp280->trimming_parameter.dig_P2 = -10685; // 测试数据
    // bmp280->trimming_parameter.dig_P3 = 3024; // 测试数据
    // bmp280->trimming_parameter.dig_P4 = 2855;     // 测试数据
    // bmp280->trimming_parameter.dig_P5 = 140;     // 测试数据
    // bmp280->trimming_parameter.dig_P6 = -7;      // 测试数据
    // bmp280->trimming_parameter.dig_P7 = 15500;   // 测试数据
    // bmp280->trimming_parameter.dig_P8 = -14600;  // 测试数据
    // bmp280->trimming_parameter.dig_P9 = 6000;    // 测试数据


    var1 = (double)t_fine / 2.0 - 64000.0; // 计算中间变量
    var2 = var1 * var1 * (double)bmp280->trimming_parameter.dig_P6 / 32768.0; // 计算中间变量
    var2 = var2 + var1 * (double)bmp280->trimming_parameter.dig_P5 * 2.0; // 计算中间变量
    var2 = (var2 / 4.0) + ((double)bmp280->trimming_parameter.dig_P4 * 65536.0); // 计算中间变量
    var1 = (((double)bmp280->trimming_parameter.dig_P3 * var1 * var1 / 524288.0) + (double)bmp280->trimming_parameter.dig_P2 * var1) / 524288.0; // 计算中间变量
    var1 = (1.0 + var1 / 32768.0) * (double)bmp280->trimming_parameter.dig_P1; // 计算中间变量
    if (var1 == 0.0)
    {
        return; // 避免除以零的错误
    }
    p_fine = 1048576.0 - (double)adc_P; // 计算p_fine
    p_fine = (p_fine - (var2 / 4096.0)) * 6250.0 / var1; // 计算中间变量
    var1 = (double)bmp280->trimming_parameter.dig_P9 * p_fine * p_fine / 2147483648.0; // 计算中间变量
    var2 = p_fine * (double)bmp280->trimming_parameter.dig_P8 / 32768.0; // 计算中间变量
    p_fine = p_fine + (var1 + var2 + (double)bmp280->trimming_parameter.dig_P7) / 16.0; // 计算实际气压值

    bmp280->measure.p_fine = p_fine; // 存储p_fine值
    bmp280->measure.pressure = (float)p_fine; // 存储气压值

    *pressure = (float)p_fine; // 返回气压值
}


/**
 * @brief 初始化BMP280驱动实例
 *
 * @param conf BMP280驱动配置结构体
 * @return BMP280DriverInstance_s* 返回BMP280驱动实例指针
 * @note 该函数用于初始化BMP280驱动实例。根据传入的配置结构体，创建BMP280驱动实例，并进行必要的初始化操作。
 */
BMP280DriverInstance_s *BMP280Driver_Init(BMP280Driver_conf_s *conf)
{
    BMP280DriverInstance_s *bmp280 = (BMP280DriverInstance_s *)malloc(sizeof(BMP280DriverInstance_s));
    memset(bmp280, 0, sizeof(BMP280DriverInstance_s)); // 初始化实例
    if (bmp280 == NULL)
    {
        return NULL; // 内存分配失败
    }
    if (conf->id >= Sensor_CNT)
    {
        free(bmp280); // 释放内存
        return NULL;  // 设备ID超出范围
    }
    bmp280->iic_instance = IICRegister(&conf->iic_conf);
    bmp280->id = conf->id;
    // if(conf->id > 0)
    // {
    //     bmp280->iic_instance =
    // }
    bmp280->work_mode = conf->work_mode;
    bmp280->p_oversampling = conf->p_oversampling;
    bmp280->t_oversampling = conf->t_oversampling;
    bmp280->filter_coefficient = conf->filter_coefficient;
    bmp280->t_sb = conf->t_sb;
    bmp280->measure.status = 0; // 初始化状态值
    // DWT_Delay(0.01);            // 延时，确保读取完成

    BMP280Calibrate(bmp280); // 校准传感器
    BMP280Reset(bmp280);     // 复位传感器

    BMP280SetOversamplingAndMode(bmp280, conf->p_oversampling, conf->t_oversampling, conf->work_mode); // 设置过采样率和工作模式
    BMP280SetStandbyFilter(bmp280, conf->filter_coefficient, conf->t_sb);                              // 设置待机时间和滤波器系数
    BMP280Instance[conf->id - 1] = bmp280;                                                             // 将实例存储到全局数组中
    return bmp280;
}